///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
/////////////////////  A MUD  Server   ////////////////////
///////////////////// By: Jared Devall ////////////////////
/////////////////////      Thanks:     ////////////////////
/////////////////////  DIKU/Merc/ROM   ////////////////////
///////////////////// Aetas/Deus Gang  ////////////////////
/////////////////////       Beej       ////////////////////
///////////////////////////////////////////////////////////

#include <string>
#include <iostream>
#include <fstream>
#include <time.h>
#include <cstdio>
#include <signal.h>
#include <list>
#include "stringutil.h"
#include "world.h"
#include "timestamp.h"

using namespace std;

// Holds the "infinite" loop that runs the mud
void World::Exist( const string &startMessage, int port, bool copyover ) {
	string buf;
    
	if ( copyover == false ) {
		SocketServer::Instance().Start();
	} else {
		CopyoverRecovery();
	}

	buf << "\n\t" << startMessage << " on port " << port;
	buf << "\n\tTime: " << Timestamp::Instance().GetDateTime();
	cout << buf << endl;

	_worldExists = true; // This is used so we can have a Character Shutdown the Server.


	// _worldExists can be set to false by shutdown; otherwise run
	while( _worldExists ) {
		Monitor();
	}

	cout << "Shutdown successfully." << endl;
	return;
}

// Saves connections and spawns a new instance of the mud
void World::Copyover( ) {
	list< Avatar *>::iterator a_it;
	Avatar * dead;
	ofstream fp("file.cpy");
	
	if ( !fp ) {
		cout << "Copyover file could not be opened." << endl;
		return;
	}

	for ( a_it = _avatarList.begin(); a_it != _avatarList.end(); ) {
		// These people aren't logged in. Kill them!
		if ( (*a_it)->GetStatus() != CONNECTED ) {
			dead = (*a_it);
			a_it = _avatarList.erase( a_it );
			
			delete dead;
			continue;
		}
		
		
		(*a_it)->Save();
		fp << (*a_it)->GetSocket()->GetDescriptor() << " " <<
			(*a_it)->Get( "name" ) << " " <<
			(*a_it)->GetSocket()->GetSocketIP() <<  endl;
		a_it++;
	}
	
	fp << "-1";
	fp.close();
	string controlSock;
	controlSock << SocketServer::Instance().GetHostSocket();	
	execl("./scratch", "2950", "-copyover", controlSock.c_str(), NULL );
}

// Sets up the connections after a copyover
void World::CopyoverRecovery( ) {
	Avatar * avatar;
	ifstream fp("file.cpy");
	int desc;
	std::string name;
	std::string host;
	std::string lhost;	

	if ( !fp ) {		
		cout << "Copyover file could not be opened." << endl;
		exit(1);
	}

	for(;;) {
		fp >> desc >> name >> host;
		if (desc == -1)
			break;
		avatar = new Avatar;
		avatar->GetSocket()->SetDescriptor( desc );
		avatar->GetSocket()->SetSocketIP( string(host) );
		avatar->Set( "name", name );
		avatar->Load();
		avatar->SetStatus( CONNECTED );
		// Add user to socket and avatar lists
		SocketServer::Instance()._socketList.push_back( avatar->GetSocket() );
		_avatarList.push_back( avatar );
		
		// We're all done. Let them know. :)
		avatar->Send( "Reboot Successful.\n\r" );
		
		// Stick them defaultly in InputHandler
		avatar->StackHandler( new InputHandler );		
	}
	fp.close();
	remove( "file.cpy" ); // Delete the temp file
	cout << "Copyover Recovery is now Complete." << endl;
}

Avatar * World::FindAvatar( const string &target ) {
	list< Avatar * >::iterator it;
	
	for ( it = _avatarList.begin(); it != _avatarList.end(); ++it ) {
		if ( str_cmp( (*it)->Get( "name" ), target ) )
			return (*it);
	}	
	return NULL;
}

// Sends a message to all avatars meeting broadcast_type
void World::Broadcast( const string &message, BROADCAST_TYPE type ) {
	list< Avatar * >::iterator avatar;

	if ( type == ALL ) {
		for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
			(*avatar)->Send( message );
		}
	} else if ( type == WCONNECTED ) {
		for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
			if ( (*avatar)->GetStatus() == CONNECTED ) 
				(*avatar)->Send( message );
		}		
	}
}

// This is the 'shutdown' of the MUD.
void World::Die( ) {
	_worldExists = false;
}

// This is where we process everything going on	
void World::Monitor( ) {
	bool newConnection = false;   
    
    FlushOutput();
    if ( ( newConnection = SocketServer::Instance().Monitor() ) == true ) {
		Avatar * avatar = new Avatar;
		avatar->SetSocket( SocketServer::Instance()._socketList.back() );
		_avatarList.push_back( avatar );
		avatar->StackHandler( new GetNameHandler );
		newConnection = false;
		avatar->Send( "    ============================================= \n\r" );
		avatar->Send( "    ===              {WScratch MUD{x              === \n\r" );
		avatar->Send( "    ============================================= \n\r\n\r" );
		avatar->Send( "                 {RIn Early Development.{x\n\r" );
		avatar->Send( "                    {W- Rendelven -{x\n\r\n\r" );
		avatar->Send( "  {R*{WNote: All passwords changed to 'password'\n\r" );
		avatar->Send( "                Change Once Connected{x\n\r" );
	} 
	UpdateTimers();
	UpdateAvatars();
	HandleInput();	
}

// Fixes the prompts for everyone
void World::FlushOutput( ) {
	list< Avatar * >::iterator avatar;

	for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
		(*avatar)->FlushOutput();
	}
}

// Checks to see if any avatars have disconnected or got input
void World::UpdateAvatars( ) {
	list< Avatar * >::iterator avatar;

	for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
		if ( (*avatar)->GetSocket()->GotInput() == true ) {
			(*avatar)->SetGotInput( true );
		}
		
		if ( (*avatar)->GetSocket()->IsDisconnected() )
			(*avatar)->SetDisconnected( true );
	}
	
	KillFlaggedAvatars();
}

// Deletes the avatars from the list and disconnects them
void World::KillFlaggedAvatars( ) {
	list< Avatar * >::iterator avatar;
	Avatar * avatar2;

	for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ) {
		avatar2 = (*avatar);
		if ( avatar2->IsDisconnected() ) {			
			avatar = _avatarList.erase( avatar );
			delete avatar2;
		} else {
			++avatar;
		}		
	}
}

// Handles all avatar input
void World::HandleInput( ) {
	std::list< Avatar * >::iterator avatar;

	// This is for handling input.
	// Loops through avatars, checks to see if there's input to play with, 
	// then goes on that.
	if ( _avatarList.empty() != true ) { 
		for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {			
			// Don't mess with input until delayed text is finished
			if ( (*avatar)->GotInput() == true /*&& (*avatar)->waiting == false*/ ) { // We need to make sure there's actually some input to handle
				(*avatar)->HandleInput( (*avatar)->GetSocket()->GetInput() ); // This sends user input to right handler.
				(*avatar)->GetSocket()->FlushInput( ); // We need to clean the inbuf or it'll loop using the same stuff.
			}
			
		}
	}

	UpdateAvatars();
}

// Updates our timers to see if they need to fire, etc
void World::UpdateTimers () {
	if ( _timers.empty() )
 		return;

	time_t now = time(NULL);
	Timer *timer;
	
	for ( timer = _timers.top(); timer->When() <= now; timer = _timers.top() ) {
		_timers.pop();

		if ( !timer->_enabled || !timer->Fire() ) {
			delete timer;
 
 		   if ( _timers.empty() ) 
 		   	break;
		} else {
			_timers.push( timer );
		}
 	}
 }

// Schedule a new timer event
void World::Schedule( Timer *timer ) {
	_timers.push( timer );
}

// disable a new timer event
void World::Unschedule( Timer *timer ) {
 	timer->_enabled = false;
}
